#/usr/bin/env python
import socket,os,binascii,hashlib,time,random
class PJLStatic:
    """
    So as to not bind up the printer port for printing
    Every function will close down the socket when command
    has been sent
    #NOTE THIS CLASS IS NOT AS UP TO DATE AS THE DYNAMIC CLASS
    #YOU SHOULD USE IT INSTEAD!!!
    """
    def __init__(self,ip,port=9100,timeout=10):
        """
        class construct
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket time out,default 10, provide as an int
        """
        self.ip      = ip
        self.port    = port
        self.timeout = timeout
        #generate a socket
        self.gen_Socket()
    
    def genRanStr(self,bytelen):
        """
        generate a random string of data
        byelen = number of bytes to generate
        returns a list of letters
        """
        letters = [] #empty list for mixed letters
        random.seed() #use system time as seed
        while len(letters) < bytelen:
            #generate some sudo random capital letters
            letters.append(chr(random.randrange(66,90,1)))
            letters.append(chr(random.randrange(97,122,1)))
            #join both lists and shuffle the letters
            random.shuffle(letters)
        return letters
    
    def genRanComment(self):
        """
        Return a randomly generated comment of between 10 and 50 chars
        Or return no comment at all
        """
        if random.choice([1,2]) == 2:
            return "@PJL COMMENT %s\r\n" % ''.join(self.genRanStr(random.randrange(10,50,1)))
        else: return None
                

    def statusoff(self):
        """
        Disable Status Messages to the printer display
        No errors returns True, errors returns False
        """
        comment = self.genRanComment()
        if comment != None:
            cmd =  "\x1b\x25\x2d12345"+comment+"X@PJL USTATUS DEVICE=OFF\r\n\x1b\x25\x2d12345X"
        else :
            cmd = "\x1b\x25\x2d12345X@PJL USTATUS DEVICE=OFF\r\n\x1b\x25\x2d12345X"
        data = self.sockSend(cmd,self.timeout)
        if data is False:
            return False
        else:
            return

    def rst(self):
        """
        Reset the printer to user default variables
        returns nothing
        """
        comment = self.genRanComment()
        if comment != None:
            cmd =  "\x1b\x25\x2d12345X"+comment+"@PJL RESET\r\n\x1b\x25\x2d12345X@PJL \r\n"
        else :
            cmd = "\x1b\x25\x2d12345X@PJL RESET\r\n\x1b\x25\x2d12345X@PJL \r\n"
        data = self.sockSend(cmd)
        if data is False:
            return False
        else:
            return

    def dskLock(self,state,passwd):
        """
        BROKEN needs to be moved to a standard s.connect, also does not use random comments
        Used to lock or unlock the Disk
        state = ON or OFF
        passwd = PJL password to use #note one must be set to lock or unlock
        returns either enabled or disabled or False for an error
        """
        if state not in ['ON','OFF']:
            return False
        if state is "ON": state = "ENABLED"
        elif state is "OFF": state = "DISABLED"
        try:
            self.s.sendall("\x1b\x25\x2d12345X@PJL \r\n@PJL JOB PASSWORD ="+passwd+"\r\n@PJL DEFAULT DISKLOCK = "+state+"\r\n@PJL DINQUIRE DISKLOCK \r\n@PJL EOJ \r\n\x1b\x25\x2d12345X")
            #return just the state of the disk lock
            recv = self.s.recv(1024).strip().split('\r\n')[1]
            return recv
        except socket.timeout,e:
            #print e
            self.s.close()
            return False
        except socket.error,e:
            #print e
            self.s.close()
            return False

    def dskInq(self):
        """
        Return Disklock is enabled or disabled only on disk0
        timeout is the socket timeout before it gives up default is 10
        returns either enabled or disabled or False for an error
        """
        x = self.dinquire('DISKLOCK')
        return x.strip().split('\r\n')[1]

    def cplock(self,state,passwd):
        """
        If the control Panel is locked it will unlock it 
        and if unlocked it will lock
        passwd = PJL password 
        state = what you wish to change either ON or OFF
        timeout is the socket timeout before it gives up default is 10
        returns either enabled or disabled or False for an error
        """
        if state.upper() not in ['ON','OFF']: return False
        comment = self.genRanComment() 
        if comment != None:
            cmd =  "\x1b\x25\x2d12345X"+comment+"@PJL \r\n@PJL JOB PASSWORD = "+passwd+"\r\n@PJL DEFAULT CPLOCK = "+state+"\r\n@PJL DINQUIRE CPLOCK \r\n@PJL EOJ \r\n\x1b\x25\x2d12345X"
        else :
            cmd = "\x1b\x25\x2d12345X@PJL \r\n@PJL JOB PASSWORD = "+passwd+"\r\n@PJL DEFAULT CPLOCK = "+state+"\r\n@PJL DINQUIRE CPLOCK \r\n@PJL EOJ \r\n\x1b\x25\x2d12345X"
        
        try:
            self.s.sendall(cmd)
            return self.s.recv(1024).strip().split('\r\n')[1]
        except socket.timeout,e:
            #print e
            self.s.close()
            return False
        except socket.error,e:
            #print e
            self.s.close()
            return False

    def cpInq(self):
        """
        Return PJL Control Pannel lock is enabled or disabled
        returns either enabled or disabled or False for an error
        """
        x = self.dinquire('CPLOCK')
        if x == False:
            return False
        else:
            try:
                return x.strip().split('\r\n')[1]
            except: return False

    def dinquire(self,item):
        """
        Return PJL some sort of status
        item = the item you wish to pass to DINQUIRE
        No errors returns the requested info , errors returns False
        """
        comment = self.genRanComment()
        if comment != None:
            cmd = "\x1b\x25\x2d12345X"+comment+"@PJL DINQUIRE "+item+"\r\n\x1b\x25\x2d12345X"
        else :
            cmd = "\x1b\x25\x2d12345X@PJL DINQUIRE "+item+"\r\n\x1b\x25\x2d12345X"
        data = self.sockSend_Recv(cmd)
        if data is False:
            return False
        else: return data

    def getModel(self):
        """
        Return printer model
        timeout is the socket timeout before it gives up default is 10
        No errors returns the model number, errors returns False
        """
        comment = self.genRanComment()
        if comment != None:
            cmd =  "\x1b\x25\x2d12345X"+comment+"@PJL INFO ID\r\n\x1b\x25\x2d12345X"
        else :
            cmd = "\x1b\x25\x2d12345X@PJL INFO ID\r\n\x1b\x25\x2d12345X"

        data = self.sockSend_Recv(cmd)
        if data is False:
            return False
        else:
            try:
                return data.strip().split('"')[1]
            except IndexError:
                return False

    def getModel2(self):
        """
        Get printers Model number differnt way
        No errors returns the model number, errors returns False
        """
        comment = self.genRanComment()
        if comment != None:
            cmd =  '\x1b\x25\x2d12345X'+comment+'@PJL DMINFO ASCIIHEX="00000401010302"\r\n\x1b\x25\x2d12345X'
        else :
            cmd = '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX="00000401010302"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(cmd)
        if data is False:
            return False
        else:
            return binascii.unhexlify(data.strip()[64:-1])
        
    def getModel3(self):
        """
        Get printers Model number differnt way
        No errors returns the model number, errors returns False
        """
        comment = self.genRanComment()
        if comment != None:
            cmd =  '\x1b\x25\x2d12345X'+comment+'@PJL DMINFO ASCIIHEX="0000040101030A"\r\n\x1b\x25\x2d12345X'
        else :
            cmd = '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX="0000040101030A"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(cmd)
        if data is False:
            return False
        else:
            return binascii.unhexlify(data.strip()[64:-1])
        
    def rdymessage(self,message):
        """
        Update the LCD Display with a ready message
        message = message to display on the screen, provide as a string
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns True, errors returns False
        """
        comment = self.genRanComment
        if comment != None:
            cmd = '\x1b\x25\x2d12345X'+comment+'@PJL RDYMSG DISPLAY = "'+message+'"\r\n\x1b\x25\x2d12345X'
        else :
            cmd = '\x1b\x25\x2d12345X@PJL RDYMSG DISPLAY = "'+message+'"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend(cmd)
        if data is False:
            return False
        else:
            return 

    def failmessage(self,message):
        """
        Update the LDC display with a fail message
        Printer can not print untill this message is cleared
        No errors returns True, errors returns False
        """
        comment = self.genRanComment
        if comment != None:
            cmd = '\x1b\x25\x2d12345X'+comment+'@PJL OPMSG DISPLAY = "'+message+'"\r\n\x1b\x25\x2d12345X'
        else :
            cmd = '\x1b\x25\x2d12345X@PJL OPMSG DISPLAY = '+message+'\r\n\x1b\x25\x2d12345X'
        data = self.sockSend(cmd)
        if data is False:
            return False
        else:
            return 


    def infofiles(self):
        """
        Pull stats on the file system
        No errors returns file system info, errors returns False
        """
        comment = self.genRanComment()
        if comment != None:
            cmd = "\x1b\x25\x2d12345X"+comment+"@PJL INFO FILESYS\r\n\x1b\x25\x2d12345X"
        else:
            cmd = "\x1b\x25\x2d12345X@PJL INFO FILESYS\r\n\x1b\x25\x2d12345X"
        data = self.sockSend_Recv(cmd)
        if data is False:
            return False
        else:
            return data
    
    def Pdir(self,FilePath):
        """
        directory list of given path
        FilePath = full filepath of the directory you want info on, 
            provide as a string
        No errors returns directory, errors returns False
        """
        data = self.sockSend_Recv(
            '\x1b\x25\x2d12345X@PJL FSDIRLIST NAME = "'+FilePath+'" ENTRY=1 COUNT=25\r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        else:
            return data
    
    def queryObject(self,item):
        """
        query file or dir to see if it exists
        item = full filepath and filename or directory 
        name you want info on, provide as a string
        No errors returns a positie file query, errors returns False
        """
        data = self.sockSend_Recv(
            '\x1b\x25\x2d12345X@PJL FSQUERY NAME = "'+item+'" \r\n\x1b\x25\x2d12345X',
            )
        if data is False:
            return False
        #return true or false if item exists?
        return data

    def uploadFiles(self,file,filepath):
        """
        upload file from the host to the printer
        file = bytes of the object to be uploaded
        filepath = full destination filepath with file name, provide as a string
        No errors returns a postive file query, errors returns False
        """
        data = self.sockSend(
            '\x1b\x25\x2d12345X@PJL FSDOWNLOAD FORMAT:BINARY SIZE='+str(len(file))+' NAME = "'+filepath+'"\r\n'+str(file)+'\x1b\x25\x2d12345X',
            )
        if data is False:
            return False
        resp = self.queryObject(filepath)
        try:
            if resp.strip().split("\r\n")[1] == "FILEERROR=3":
                return False #return false
        except Exception:
            pass

    def delete(self,file):
        """
        delete files or directories
        file = filename of the file to upload
        No errors returns a failed file query, errors returns False
        """
        data = self.sockSend(
            '\x1b\x25\x2d12345X@PJL FSDELETE NAME = "'+file+'"\r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        else:
            return self.queryObject(file)

    def filesys(self):
        """
        Returns Total Capacity, Volume Label, Free Space
        Volume read/write status, Location
        No errors returns the file system info
        """
        data = self.sockSend_Recv(
            '\x1b\x25\x2d12345X@PJL INFO FILESYS \r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        else: return data

    def mkdir(self,name):
        """
        make a directory
        name = full directory path and 
            name of directory to create, provide as a string
        No errors returns a positive query object
        """
        data = self.sockSend(
            '\x1b\x25\x2d12345X@PJL FSMKDIR NAME = "'+name+'"\r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        else:
            return self.queryObject(name) #check to see if dir was created

    def downloadFiles(self,file,offset='0'):
        """
        download files off the printer to the host
        name = name to write the file to the hdd with
        file = full destination filepath with file name, provide as a string
        offset is the byte to start reading from
        No errors returns a file written out, errors returns False
        """
        resp = self.queryObject(file)
        try:
            if resp.strip().split("\r\n")[1] == "FILEERROR=3":
                return False #return false
        except Exception:
            pass
        try:
            size = resp.split(" ")[-1].strip().split("=")[1]
        except AttributeError:
            return False
        object = ""
        #calc header size store at gg
        gg = len('@PJL FSUPLOAD FORMAT:BINARY NAME="'+file+'" OFFSET='+str(offset)+' SIZE='+size+'\r\n')
        try:
            self.s.sendall('\x1b\x25\x2d12345X@PJL FSUPLOAD NAME = "'+file+'" OFFSET='+str(offset)+' SIZE='+size+'\r\n\x1b\x25\x2d12345X')
        except socket.timeout,e:
            #print e
            self.s.close()
            return False
        except socket.error,e:
            #print e
            self.s.close()
            return False
        while True: #loop though and open up sockets till we have all the data
            try:
                object += self.s.recv(6144) #leave a nice big buffer
                if len(object)+gg >= int(size):
                    #check to see if we recv enough data to quit the loop
                    #size is the expected file size, gg is the header
                    break
            except socket.timeout,e:
                self.s.close()
                return False
            except socket.error,e:
                self.s.close()
                return False
        #figure out the len of the header to strip off
        object = object[gg:] #strip off the responce header
        if object[-1] == "\x0c":
            fobject = object[:-1] #strip off extra from feed byte
        else:
            fobject = object
        return fobject
    
    def reboot(self,passwd=0):
        """
        reboots the printer and closes the socket connection
        may not work on all printers tested on a 4250 and 4350
        passwd = PJL password as a str, see *NOTE*
        returns the output of the DMINFO
        *NOTE* if a printer has a PJL password enabled 
        then the correct one is requred to reboot the device
        """
        #prtgeneralreset object = 040006020501010301040105
        data = self.sockSend_Recv(
            '\x1b\x25\x2d12345X@PJL \r\n@PJL JOB PASSWORD = '+str(passwd)+' \r\n@PJL DMINFO ASCIIHEX="040006020501010301040105"\r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        else: return data

    def initialize(self):
        """
        Reset many of the PJL settings
        """
        self.sockSend(
            '\x1b\x25\x2d12345X@PJL INITIALIZE\r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        else: return 

    def getSerial2(self):
        """
        Get printers serial number
        returns serial number as a string or Fase on error
        """
        data = self.sockSend_Recv(
            '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX="00000401010303"\r\n\x1b\x25\x2d12345X')
        if data is False:
            return False
        return binascii.unhexlify(data.strip()[64:-1])

    def getSerial(self):
        """
        Get printers serial number another way
        returns serial number as a string or Fase on error
        """
        data = self.sockSend_Recv(
            '\x1b\x25\x2d12345X@PJL INQUIRE SERIALNUMBER\r\n\x1b\x25\x2d12345X')
        if data is False: 
            return False
        try:
            return data.strip().split('\r\n')[1]
        except IndexError:
            return False

    def sockSend(self,string):
        """
        A genric socket send function
        string = string to send
        returns either False or raw unparsed output
        if False it means the socket has closed and must be re opened
        """
        try:
            self.s.sendall(string)
            return None
        except socket.timeout,e:
            self.s.close()
            return False
        except socket.error,e:
            self.s.close()
            return False
    
    def sockSend_Recv(self,string):
        """
        A genric socket send function with recv
        string = string to send
        returns either False or raw unparsed output
        if False it means the socket has closed and must be re opened
        """
        try:
            self.s.sendall(string)
            x = self.s.recv(1024)
            return x
        except socket.timeout,e:
            self.s.close()
            return False
        except socket.error,e:
            self.s.close()
            return False
    
    def gen_Socket(self):
        """
        generate or regerate a socket for use by other functions in the class
        """
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.settimeout(self.timeout)
        self.s.connect((self.ip,self.port))

class PJLDynamic:
    """
    So as to not bind up the printer port for printing
    Every function will close down the socket when command
    has been sent
    """
    def __init__(self,ranComment=False):
        """
        ranComment determins if random payload comments are sent
        """
        self.ranComment = ranComment
    
    def genRanStr(self,bytelen):
        """
        generate a random string of data
        byelen = number of bytes to generate
        """
        letters = [] #empty list for mixed letters
        random.seed() #use system time as seed
        while len(letters) < bytelen:
            #generate some sudo random capital letters
            letters.append(chr(random.randrange(65,90,1)))
            letters.append(chr(random.randrange(97,122,1)))
            #join both lists and shuffle the letters
            random.shuffle(letters)
        return letters

    def genRanComment(self):
        """
        Return a randomly generated comment of between 10 and 50 chars
        Or return no comment at all
        """
        if self.ranComment == True:
            if random.choice([1,2]) == 2:
                return "@PJL COMMENT %s\r\n" % ''.join(self.genRanStr(random.randrange(50,256,1)))
            else: return None
        else: return None
    
    def genCommand(self,cmd):
        """
        Generate the command for the printer
        """
        comment = self.genRanComment()
        if comment != None:
            return cmd[:9]+comment+cmd[9:]
        else:
            return cmd
                
    def statusoff(self,ip,port,timeout=10):
        """
        Disable Status Messages to the printer display
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10, provide as an int
        No errors returns True, errors returns False
        """
        cmd = "\x1b\x25\x2d12345X@PJL USTATUS DEVICE=OFF\r\n\x1b\x25\x2d12345X"
        data = self.sockSend(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return

    def rst(self,ip,port,timeout=10):
        """
        Reset the printer to user default variables
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10
        returns nothing
        """
        cmd = "\x1b\x25\x2d12345X@PJL RESET\r\n\x1b\x25\x2d12345X@PJL \r\n"
        data = self.sockSend(ip,port,self.genCommand(cmd),timout)
        if data is False:
            return False
        else:
            return

    def dskLock(self,ip,port,state,passwd,timeout=10):
        """
        Used to lock or unlock the Disk
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        state = ON or OFF
        passwd = PJL password to use #note one must be set to lock or unlock
        timeout is the socket timeout before it gives up default is 10
        returns either enabled or disabled or False for an error
        """
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        if state not in ['ON','OFF']:
            return False
        #if state is "ON": state = "ENABLED" #shouldnt be needed
        #elif state is "OFF": state = "DISABLED"
        cmd = "\x1b\x25\x2d12345X@PJL \r\n@PJL JOB PASSWORD ="+passwd+"\r\n@PJL DEFAULT DISKLOCK = "+state+"\r\n@PJL DINQUIRE DISKLOCK \r\n@PJL EOJ \r\n\x1b\x25\x2d12345X"
        try:
            s.connect((ip,port))
            s.sendall(self.genCommand(cmd))
            #return just the state of the disk lock
            recv = s.recv(1024).strip().split('\r\n')[1]
            s.close()
            return recv
        except socket.timeout,e:
            #print e
            return False
        except socket.error,e:
            #print e
            return False

    def dskInq(self,ip,port,timeout=10):
        """
        Return Disklock is enabled or disabled only on disk0
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10
        returns either enabled or disabled or False for an error
        """
        x = self.dinquire('DISKLOCK',ip,port,timeout)
        return x.strip().split('\r\n')[1]

    def cplock(self,ip,port,state,passwd,timeout=10):
        """
        If the control Panel is locked it will unlock it 
        and if unlocked it will lock
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        passwd = PJL password 
        state = what you wish to change either ON or OFF
        timeout is the socket timeout before it gives up default is 10 seconds
        returns either enabled or disabled or False for an error
        """
        if state.upper() not in ['ON','OFF']: return False
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        cmd = "\x1b\x25\x2d12345X@PJL \r\n@PJL JOB PASSWORD = "+passwd+"\r\n@PJL DEFAULT CPLOCK = "+state+"\r\n@PJL DINQUIRE CPLOCK \r\n@PJL EOJ \r\n\x1b\x25\x2d12345X"
        try:
            s.connect((ip,port))
            s.sendall(self.genCommand(cmd))
            x = s.recv(1024).strip().split('\r\n')[1]
            s.close()
            return x.strip().split('\r\n')
        except socket.timeout,e:
            #print e
            return False
        except socket.error,e:
            #print e
            return False


    def cpInq(self,ip,port,timeout=10):
        """
        Return PJL Control Pannel lock is enabled or disabled
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10
        returns either enabled or disabled or False for an error
        """
        x = self.dinquire('CPLOCK',ip,port,timeout)
        if x == False:
            return False
        else:
            try:
                return x.strip().split('\r\n')[1]
            except: return False

    def dinquire(self,item,ip,port,timeout=10):
        """
        Return PJL some sort of status
        item = the item you wish to pass to DINQUIRE
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10
        No errors returns the requested info , errors returns False
        """
        cmd = "\x1b\x25\x2d12345X@PJL DINQUIRE "+item+"\r\n\x1b\x25\x2d12345X"
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else: return data
    
    def wakeup(self,ip,port,timeout=10):
        cmd = '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX = "04000401010102040101" \r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else: return data

    def getModel(self,ip,port,timeout=10):
        """
        Return printer model
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10
        No errors returns the model number, errors returns False
        """
        cmd = "\x1b\x25\x2d12345X@PJL INFO ID\r\n\x1b\x25\x2d12345X"
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            try:
                return data.strip().split('"')[1]
            except IndexError:
                return False

    def getModel2(self,ip,port,timeout=10):
        """
        Get printers Model number differnt way
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns the model number, errors returns False
        """
        cmd = '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX="00000401010302"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return binascii.unhexlify(data.strip()[64:-1])
        
    def getModel3(self,ip,port,timeout=10):
        """
        Get printers Model yet another differnt way
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns the model number, errors returns False
        """
        cmd = '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX="0000040101030A"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return binascii.unhexlify(data.strip()[64:-1])
    
    def rdymessage(self,message,ip,port,timeout=10):
        """
        Update the LCD Display with a ready message
        message = message to display on the screen, provide as a string
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns True, errors returns False
        """
        data = self.sockSend(ip,port,
            '\x1b\x25\x2d12345X@PJL RDYMSG DISPLAY = "'+str(message)+'"\r\n\x1b\x25\x2d12345X',
            timeout)
        if data is False:
            return False
        else:
            return 

    def failmessage(self,message,ip,port,timeout=10):
        """
        Update the LDC display with a fail message
        Printer can not print untill this message
        is cleared
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns True, errors returns False
        """
        data = self.sockSend(ip,port,
            '\x1b\x25\x2d12345X@PJL OPMSG DISPLAY = "'+str(message)+'"\r\n\x1b\x25\x2d12345X',
            timeout)
        if data is False:
            return False
        else:
            return 

    def infofiles(self,ip,port,timeout=10):
        """
        Pull stats on the file system
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns file system info, errors returns False
        """
        cmd = "\x1b\x25\x2d12345X@PJL INFO FILESYS\r\n\x1b\x25\x2d12345X"
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return data
    
    def Pdir(self,FilePath,ip,port,timeout=10):
        """
        directory list of given path
        FilePath = full filepath of the directory you want info on, 
            provide as a string
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket timeout default is 10 provide as an int
        No errors returns directory, errors returns False
        """
        cmd = '\x1b\x25\x2d12345X@PJL FSDIRLIST NAME = "'+FilePath+'" ENTRY=1 COUNT=25\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return data
    
    def queryObject(self,item,ip,port,timeout=10):
        """
        query file or dir to see if it exists
        item = full filepath and filename or directory name you want info on, 
            provide as a string
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket timeout ,default is 10 provide as an int
        No errors returns a positie file query, errors returns False
        """
        cmd = '\x1b\x25\x2d12345X@PJL FSQUERY NAME = "'+item+'" \r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        #return true or false if item exists?
        return data

    def uploadFiles(self,file,filepath,ip,port,timeout=10):
        """
        upload file from the host to the printer
        file = bytes of the object to be uploaded
        filepath = full destination filepath with file name, provide as a string
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns a postive file query, errors returns False
        #to do
        #random comments seem to break file upload code note this for future
        """
        
        data = self.sockSend(ip,port,
            '\x1b\x25\x2d12345X@PJL FSDOWNLOAD FORMAT:BINARY SIZE='+str(len(file))+' NAME = "'+filepath+'"\r\n'+str(file)+'\x1b\x25\x2d12345X'
            ,timeout)
        if data is False:
            return False
        resp = self.queryObject(filepath,ip,port)
        try:
            if resp.strip().split("\r\n")[1] == "FILEERROR=3":
                return False #return false
        except Exception:
            pass

    def delete(self,file,ip,port,timeout=10):
        """
        delete files or directories
        file = filename of the file to upload
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        No errors returns a failed file query, errors returns False
        """
        cmd = '\x1b\x25\x2d12345X@PJL FSDELETE NAME = "'+file+'"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return self.queryObject(file,ip,port)

    def filesys(self,ip,port,timeout=10):
        """
        Returns Total Capacity, Volume Label, Free Space
        Volume read/write status, Location
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket timeout, default is 10 provide as an int
        No errors returns the file system info
        """
        cmd = '\x1b\x25\x2d12345X@PJL INFO FILESYS \r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else: return data

    def appendFile(self,name,data,ip,port,timeout=10):
        """
        make a directory
        name = full directory path and name of file to append to
        data = data to append
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket timeout, default is 10 provide as an int
        No errors returns a positive query object
        """
        cmd = '\x1b\x25\x2d12345X@PJL APPEND FORMAT:BINARY SIZE='+str(len(data))+' NAME= "'+name+'" \r\n'+data+'\x1b\x25\x2d12345X'
        data = self.sockSend(ip,port,self.genCommand(cmd),timeout)
        print data
        if data is False:
            return False
        else:
            return self.queryObject(name,ip,port) #check to see if dir was created

    def mkdir(self,name,ip,port,timeout=10):
        """
        make a directory
        name = full directory path and 
            name of directory to create, provide as a string
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket timeout, default is 10 provide as an int
        No errors returns a positive query object
        """
        cmd = '\x1b\x25\x2d12345X@PJL FSMKDIR NAME = "'+name+'"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else:
            return self.queryObject(name,ip,port) #check to see if dir was created

    def downloadFiles(self,file,ip,port,timeout=10,offset='0'):
        """
        download files off the printer to the host
        name = name to write the file to the hdd with
        file = full destination filepath with file name, provide as a string
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        offset is the byte to start reading from
        No errors returns a file written out, errors returns False
        #to do
        #random comments seem to break file upload code note this for future
        """
        resp = self.queryObject(file,ip,port)
        try:
            if resp.strip().split("\r\n")[1] == "FILEERROR=3":
                return False #return false
        except Exception:
            pass
        try:
            size = resp.split(" ")[-1].strip().split("=")[1]
        except AttributeError:
            return False
        object = ""
        #calc header size store at gg
        gg = len('@PJL FSUPLOAD FORMAT:BINARY NAME="'+file+'" OFFSET='+str(offset)+' SIZE='+size+'\r\n')
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.settimeout(timeout)
            s.connect((ip,port))
            s.sendall('\x1b\x25\x2d12345X@PJL FSUPLOAD NAME = "'+file+'" OFFSET='+str(offset)+' SIZE='+size+'\r\n\x1b\x25\x2d12345X')
        except socket.timeout,e:
            #print e
            s.close()
            return False
        except socket.error,e:
            #print e
            s.close()
            return False
        while True: #loop though and open up sockets till we have all the data
            try:
                object += s.recv(6144) #leave a nice big buffer
                if len(object)+gg >= int(size):
                    #check to see if we recv enough data to quit the loop
                    #size is the expected file size, gg is the header
                    break
            except socket.timeout,e:
                s.close()
                return False
            except socket.error,e:
                s.close()
                return False
        s.close()
        #figure out the len of the header to strip off
        object = object[gg:] #strip off the responce header
        if object[-1] == "\x0c":
            fobject = object[:-1] #strip off extra from feed byte
        else:
            fobject = object
        return fobject
    
    def reboot(self,ip,port,passwd=0,timeout=10):
        """
        reboots the printer and closes the socket connection
        may not work on all printers tested on a 4250 and 4350
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = socket timeout, default is 10 provide as an int
        passwd = PJL password as a str, see *NOTE*
        returns the output of the DMINFO
        *NOTE* if a printer has a PJL password enabled 
        then the correct one is requred to reboot the device
        """
        #prtgeneralreset object = 040006020501010301040105
        cmd = '\x1b\x25\x2d12345X@PJL \r\n@PJL JOB PASSWORD = '+str(passwd)+' \r\n@PJL DMINFO ASCIIHEX="040006020501010301040105"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else: return data

    def initialize(self,ip,port,timeout=10):
        """
        Reset many of the PJL settings
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        """
        cmd = '\x1b\x25\x2d12345X@PJL INITIALIZE\r\n\x1b\x25\x2d12345X'
        self.sockSend(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        else: return 

    def getSerial2(self,ip,port,timeout=10):
        """
        Get printers serial number
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout is the socket timeout before it gives up default is 10 provide as an int
        returns serial number as a string or Fase on error
        """
        cmd = '\x1b\x25\x2d12345X@PJL DMINFO ASCIIHEX="00000401010303"\r\n\x1b\x25\x2d12345X'
        data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        if data is False:
            return False
        return binascii.unhexlify(data.strip()[64:-1])

    def getSerial(self,ip,port,timeout=10):
        """
        Get printers serial number another way
        ip = ip address to connect to, provide as a string
        port = port to connect on, provide as an int
        timeout = is the socket timeout default is 10 provide as an int
        returns serial number as a string or Fase on error
        """
        cmd = '\x1b\x25\x2d12345X@PJL INQUIRE SERIALNUMBER\r\n\x1b\x25\x2d12345X'
        #data = self.sockSend_Recv(ip,port,self.genCommand(cmd),timeout)
        data = self.sockSend_Recv(ip,port,cmd,timeout)
        if data is False: 
            return False
        try:
            return data.strip().split('\r\n')[1]
        except IndexError:
            return False

    def sockSend(self,ip,port,string,timeout=10):
        """
        A genric socket send function
        ip = ip address to connect to, provide as a string
        port = port to connect to, provide as an int
        string = string to send
        timeout = socket time out as an int, 10sec is default
        returns either False or raw unparsed output
        """
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        try:
            s.connect((ip,port))
            s.sendall(string)
            s.close()
            return None
        except socket.timeout,e:
            s.close()
            return False
        except socket.error,e:
            s.close()
            return False
    
    def sockSend_Recv(self,ip,port,string,timeout=10):
        """
        A genric socket send function with recv
        ip = ip address to connect to, provide as a string
        port = port to connect to, provide as an int
        string = string to send
        timeout = socket timeout as an int, 10sec is default
        returns either False or raw unparsed output
        """
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        try:
            s.connect((ip,port))
            s.sendall(string)
            x = s.recv(1024)
            s.close()
            return x
        except socket.timeout,e:
            s.close()
            return False
        except socket.error,e:
            s.close()
            return False
